/*
 * Bucket.h
 *
 */

#ifndef BUCKET_H_
#define BUCKET_H_

#include <map>
#include "../ISerializable.h"
#include "../../log/Logger.h"
#include "../../utils/Utils.h"

template<class T>
class Bucket : public ISerializable {
private:
	typedef std::map<BigInt, T*> HashMap;
	BigInt dispersion;
	HashMap records;

public:
	typedef typename HashMap::const_iterator ConstHashIter;
	typedef typename HashMap::iterator HashIter;

	Bucket() : dispersion(1) {}
	Bucket(BigInt dispersion) : dispersion(dispersion) {}
	virtual ~Bucket() {}

	BigInt getDispersion() const;
	void duplicateDispersion();
	void divideDispersion();
	void setDispersion(BigInt dispersion);

	bool add(const T& record);
	bool remove(BigInt hashcode);
	bool find(BigInt hashcode, T& record) const;

	ConstHashIter begin() const;
	ConstHashIter end() const;

	BigInt count() const;

	BigInt size() const;
	void serialize(ISerializer& serializer) const;
	void unserialize(IUnserializer& unserializer);
};

template<class T>
BigInt Bucket<T>::getDispersion() const {
	return dispersion;
}

template<class T>
void Bucket<T>::setDispersion(BigInt dispersion) {
	this->dispersion = dispersion;
}

template<class T>
void Bucket<T>::duplicateDispersion() {
	dispersion *= 2;
}

template<class T>
void Bucket<T>::divideDispersion() {
	dispersion /= 2;
}

template<class T>
bool Bucket<T>::add(const T& record) {
	if (records.find(record.getHashcode()) != records.end()) {
		return false;
	}
	records[record.getHashcode()] = new T(record);
	Logger::getInstance().debug("Bucket: agregado el registro con hashcode = " + Utils::toString(record.getHashcode()));
	return true;
}

template<class T>
bool Bucket<T>::remove(BigInt hashcode) {
	HashIter it = records.find(hashcode);
	if (it == records.end()) {
		return false;
	}
	delete it->second;
	records.erase(it);
	Logger::getInstance().debug("Bucket: borrado el registro con hashcode = " + Utils::toString(hashcode));
	return true;
}

template<class T>
bool Bucket<T>::find(BigInt hashcode, T& record) const {
	ConstHashIter it = records.find(hashcode);
	if (records.find(hashcode) == records.end()) {
		return false;
	}
	record = *it->second;
	return true;
}

template<class T>
BigInt Bucket<T>::count() const {
	return records.size();
}

template<class T>
typename Bucket<T>::ConstHashIter Bucket<T>::begin() const {
	return records.begin();
}

template<class T>
typename Bucket<T>::ConstHashIter Bucket<T>::end() const {
	return records.end();
}

template<class T>
BigInt Bucket<T>::size() const {
	BigInt size = 2 * sizeof(BigInt);
	for (ConstHashIter it = records.begin(); it != records.end(); ++it) {
		size += it->second->size();
	}
	return size;
}

template<class T>
void Bucket<T>::serialize(ISerializer& serializer) const {
	serializer.serializeUnsignedLongInt(dispersion);
	serializer.serializeUnsignedLongInt(records.size());
	for (ConstHashIter it = records.begin(); it != records.end(); ++it) {
		it->second->serialize(serializer);
	}
}

template<class T>
void Bucket<T>::unserialize(IUnserializer& unserializer) {
	records.clear();
	dispersion = unserializer.unserializeUnsignedLongInt();
	if (dispersion == 0)
		++dispersion;
	BigInt count = unserializer.unserializeUnsignedLongInt();
	T* record;
	String recordPrint;
	for (BigInt x = 0; x < count; x++) {
		record = new T();
		record->unserialize(unserializer);
		records[record->getHashcode()] = record;
		recordPrint += "[" + Utils::toString(record->getHashcode()) + "]";
	}
	Logger::getInstance().debug("Bucket: leido " + Utils::toString(dispersion) + "|" + recordPrint);
}

#endif /* BUCKET_H_ */
